/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cardgame.gui;

import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.image.ImageObserver;
import java.awt.dnd.DragSource;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import java.awt.geom.AffineTransform;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.concurrent.ConcurrentHashMap;
import cardgame.*;
import cardgame.game.*;
import cardgame.game.commands.*;

/**
 *
 * @author Carl
 */
public class PanField extends javax.swing.JPanel{

    public PanField(final CommandSender commandSender, final CardInfoFrame cardInfoFrame){
        initComponents();
        this.commandSender = commandSender;
        this.cardInfoFrame = cardInfoFrame;
        addMouseListener(new MouseAdapter(){

            @Override
            public void mousePressed(MouseEvent evt){
                super.mousePressed(evt);
                if(isInputAllowed()){
                    CardChooser activeCardChooser = cardChooserManager.getActiveCardChooser();
                    if(evt.getModifiers() == MouseEvent.BUTTON3_MASK){
                        if(activeCardChooser != null){
                            cardChooserManager.stop();
                        }
                        cancelDragging(evt);
                    }
                    else{
                        CardDisplay cardDisplay = getHoveredCardDisplay(evt);
                        if(cardDisplay != null){
                            Card card = cardDisplay.getCard();
                            if(activeCardChooser != null){
                                if(activeCardChooser.isValidChoice(card)){
                                    int cardIndex = card.getCardPosition().getIndex();
                                    if(activeCardChooser instanceof ManaChooser){
                                        ManaChooser manaChooser = (ManaChooser) activeCardChooser;
                                        manaChooser.addManaProvider(card);
                                        checkManaChooser(manaChooser);
                                    }
                                    else if(activeCardChooser instanceof SpellParameterChooser){
                                        SpellParameterChooser spellParameterChooser = (SpellParameterChooser) activeCardChooser;
                                        spellParameterChooser.setNextParameter(card);
                                        sendSpellCastIfChoosingHasFinished();
                                    }
                                    else if(activeCardChooser instanceof ThrowingOffChooser){
                                        ThrowingOffChooser throwingOffChooser = (ThrowingOffChooser) activeCardChooser;
                                        if(card.getOwner().getHand().size() <= (game.getGameMode().getMaximumHandCards(game.getCurrentPlayer()) + 1)){
                                            cardChooserManager.stop();
                                        }
                                        commandSender.sendCommand(new ThrowingOffCommand(cardIndex));
                                    }
                                }
                            }
                            else if(isDraggingActionAllowed(card)){
                                draggedCardDisplay = cardDisplay;
                                draggedCardDisplay.saveTempLocation();
                                updateCursor();
                            }
                            else if(bot != null){
                                if((card.getOwner().getIndex() == playerIndex) && (card.getCardPosition().getZone() == CardPosition.Zone.DECK)){
                                    bot.performTurn();
                                }
                            }
                        }
                    }
                }
            }
    
            private void sendSpellCastIfChoosingHasFinished(){
                SpellParameterChooser spellParameterChooser = (SpellParameterChooser) cardChooserManager.getActiveCardChooser();
                if(spellParameterChooser.isFinished()){
                    switch(spellParameterChooser.getType()){
                        case MONSTER_SPELL:
                            int monsterCardIndex = spellParameterChooser.getChooseInformation()[0];
                            int spellIndex = spellParameterChooser.getChooseInformation()[1];
                            commandSender.sendCommand(new CastMonsterSpellCommand(monsterCardIndex, spellIndex, spellParameterChooser.getSpell_ManaIndices(), spellParameterChooser.getSpellParameters()));
                            break;

                        case SPELL:
                            int handCardIndex = spellParameterChooser.getChooseInformation()[0];
                            commandSender.sendCommand(new CastSpellCommand(handCardIndex, spellParameterChooser.getSpell_ManaIndices(), spellParameterChooser.getSpellParameters()));
                            break;
                    }
                    cardChooserManager.stop();
                }
            }

            @Override
            public void mouseReleased(MouseEvent evt){
                super.mouseReleased(evt);
                if(isInputAllowed()){
                    CardDisplay hoveredCardDisplay = getHoveredCardDisplay(evt);
                    if(draggedCardDisplay != null){
                        Card card = draggedCardDisplay.getCard();
                        int y = getGUI_Y(evt.getY());
                        if(card.getCardPosition().getZone() == CardPosition.Zone.HAND){
                            boolean isHoveringField = (y < 2000);
                            if(isHoveringField){
                                if(card instanceof ManaProvider){
                                    commandSender.sendCommand(new AddManaCommand(card.getCardPosition().getIndex()));
                                }
                                else if(card instanceof MonsterCard){
                                    MonsterCard monsterCard = (MonsterCard) card;
                                    int handCardIndex = card.getCardPosition().getIndex();
                                    if(monsterCard.getSummonCost().getMana().isEmpty()){
                                        commandSender.sendCommand(new SummonMonsterCommand(handCardIndex, new int[0]));
                                    }
                                    else{
                                        cardChooserManager.startChoosing_SummonMonsterCost(playerIndex, monsterCard, handCardIndex);
                                        chooseManaAutomatically(monsterCard.getSummonCost().getMana());
                                    }
                                }
                                else if(card instanceof SpellCard){
                                    SpellCard spellCard = (SpellCard) card;
                                    Spell spell = spellCard.getSpell();
                                    if(spell.getCost().getMana().isEmpty()){
                                        cardChooserManager.startChoosing_SpellParameter(spell, spellCard.getCardPosition().getIndex(), new int[0]);
                                        sendSpellCastIfChoosingHasFinished();
                                    }
                                    else{
                                        cardChooserManager.startChoosing_SpellCost(playerIndex, spell, spellCard.getCardPosition().getIndex());
                                        chooseManaAutomatically(spell.getCost().getMana());
                                    }
                                }
                            }
                        }
                        else if(card.getCardPosition().getZone() == CardPosition.Zone.MONSTER){
                            boolean isHoveringEnemy = (y < 1200);
                            if(isHoveringEnemy){
                                int enemyMonstersCount = game.getPlayer(playerIndex).getEnemy().getMonsters().size();
                                if(enemyMonstersCount == 0){
                                    commandSender.sendCommand(new AttackPlayerCommand(card.getCardPosition().getIndex()));
                                }
                                else{
                                    int targetMonsterIndex = -1;
                                    if(enemyMonstersCount == 1){
                                        targetMonsterIndex = 0;
                                    }
                                    else if((hoveredCardDisplay != null) && (hoveredCardDisplay.getCard().getCardPosition().getZone() == CardPosition.Zone.MONSTER) && (hoveredCardDisplay.getCard().getOwner().getIndex() != playerIndex)){
                                        targetMonsterIndex = hoveredCardDisplay.getCard().getCardPosition().getIndex();
                                    }
                                    if(targetMonsterIndex != -1){
                                        commandSender.sendCommand(new AttackMonsterCommand(card.getCardPosition().getIndex(), targetMonsterIndex));
                                    }
                                }
                            }
                        }
                        cancelDragging(evt);
                    }
                    else if(evt.getModifiers() == MouseEvent.BUTTON3_MASK){
                        if(hoveredCardDisplay != null){
                            Card card = hoveredCardDisplay.getCard();
                            if((card.getOwner().getIndex() == playerIndex) && (card.getCardPosition().getZone() == CardPosition.Zone.MONSTER)){
                                MonsterCard monsterCard = (MonsterCard) card;
                                if(monsterCard.getSpells().length > 0){
                                    int spellIndex = 0;
                                    Spell spell = monsterCard.getSpells()[spellIndex];
                                    if(monsterCard.getOwner().canCast(spell)){
                                        if(spell.getCost().getMana().isEmpty()){
                                            cardChooserManager.startChoosing_MonsterSpellParameter(spell, card.getCardPosition().getIndex(), spellIndex, new int[0]);
                                            sendSpellCastIfChoosingHasFinished();
                                        }
                                        else{
                                            cardChooserManager.startChoosing_MonsterSpellCost(playerIndex, spell, card.getCardPosition().getIndex(), spellIndex);
                                            chooseManaAutomatically(spell.getCost().getMana());
                                        }
                                    }
                                }
                            }
                        }
                    }
                    else if(isEndTurnButtonHovered){
                        if(cardChooserManager.getActiveCardChooser() != null){
                            cardChooserManager.stop();
                        }
                        else{
                            if(game.getPlayer(game.getCurrentPlayerIndex()).getHand().size() > game.getGameMode().getMaximumHandCards(game.getCurrentPlayer())){
                                cardChooserManager.startChoosing_ThrowingOff(game.getCurrentPlayerIndex());
                            }
                            else{
                                isEndTurnButtonHovered = false;
                            }
                            commandSender.sendCommand(new EndTurnCommand());
                        }
                    }
                }
            }
            
            private void cancelDragging(MouseEvent evt){
                if(draggedCardDisplay != null){
                    draggedCardDisplay.targetTempLocation();
                    draggedCardDisplay = null;
                    updateHoverInformation(evt);
                }
            }
            
            private void chooseManaAutomatically(ManaAmount manaAmount){
                ManaChooser manaChooser = (ManaChooser) cardChooserManager.getActiveCardChooser();
                ManaPool manaPool = game.getPlayer(playerIndex).getManaPool();
                boolean isMonochromeMana = true;
                Mana monochromeMana = null;
                for(int i=0;i<manaPool.size();i++){
                    ManaProvider manaProvider = (ManaProvider) manaPool.get(i);
                    Mana mana = manaProvider.getProvidedMana().getUsedManaTypes()[0];
                    if(monochromeMana == null){
                        monochromeMana = mana;
                    }
                    else if(mana != monochromeMana){
                        isMonochromeMana = false;
                        break;
                    }
                }
                for(Mana mana : Mana.values()){
                    if((mana != Mana.CUSTOM) || isMonochromeMana || (manaAmount.getAmount() == manaPool.getAvailableAmount().getAmount())){
                        int remainingAmount = manaAmount.getMana(mana);
                        if(remainingAmount != 0){
                            for(int i=0;i<manaPool.size();i++){
                                Card manaCard = manaPool.get(i);
                                if(manaChooser.isValidChoice(manaCard)){
                                    manaChooser.addManaProvider(manaCard);
                                    remainingAmount--;
                                    if(remainingAmount <= 0){
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
                checkManaChooser(manaChooser);
            }
            
            private void checkManaChooser(ManaChooser manaChooser){
                if(manaChooser.isFinished()){
                    switch(manaChooser.getType()){
                        case MONSTER_SUMMON_COST:
                            int handCardIndex_1 = manaChooser.getChooseInformation()[0];
                            commandSender.sendCommand(new SummonMonsterCommand(handCardIndex_1, manaChooser.getManaIndices()));
                            cardChooserManager.stop();
                            break;

                        case MONSTER_SPELL_COST:
                            int monsterCardIndex = manaChooser.getChooseInformation()[0];
                            int spellIndex = manaChooser.getChooseInformation()[1];
                            MonsterCard monsterCard = (MonsterCard) game.getPlayer(playerIndex).getMonsters().get(monsterCardIndex);
                            Spell spell_1 = monsterCard.getSpells()[spellIndex];
                            cardChooserManager.startChoosing_MonsterSpellParameter(spell_1, monsterCardIndex, spellIndex, manaChooser.getManaIndices());
                            sendSpellCastIfChoosingHasFinished();
                            break;

                        case SPELL_COST:
                            int handCardIndex_2 = manaChooser.getChooseInformation()[0];
                            SpellCard spellCard = (SpellCard) game.getPlayer(playerIndex).getHand().get(handCardIndex_2);
                            Spell spell_2 = spellCard.getSpell();
                            cardChooserManager.startChoosing_SpellParameter(spell_2, handCardIndex_2, manaChooser.getManaIndices());
                            sendSpellCastIfChoosingHasFinished();
                            break;
                    }
                }
            }
        });
        addMouseMotionListener(new MouseMotionAdapter(){

            @Override
            public void mouseMoved(MouseEvent evt){
                super.mouseMoved(evt);
                if(hoveredCardDisplay != null){
                    hoveredCardDisplay.setIsHighlighted(false);
                    hoveredCardDisplay = null;
                }
                CardDisplay cardDisplay = getHoveredCardDisplay(evt);
                cardInfoFrame.setCardDisplay(cardDisplay);
                if(isInputAllowed()){
                    updateHoverInformation(evt);
                    if(cardDisplay != null){
                        if(((cardChooserManager.getActiveCardChooser() == null) && isDraggingActionAllowed(cardDisplay.getCard()))
                        || ((cardChooserManager.getActiveCardChooser() != null) && cardChooserManager.getActiveCardChooser().isValidChoice(cardDisplay.getCard()))){
                            hoveredCardDisplay = cardDisplay;
                            hoveredCardDisplay.setIsHighlighted(true);
                        }
                    }
                }
            }

            @Override
            public void mouseDragged(MouseEvent evt){
                super.mouseDragged(evt);
                if(isInputAllowed()){
                    updateHoverInformation(evt);
                    if(draggedCardDisplay != null){
                        int x = (getGUI_X(evt.getX()) - (CardDisplay.WIDTH / 2));
                        int y = (getGUI_Y(evt.getY()) - (CardDisplay.HEIGHT / 2));
                        draggedCardDisplay.setLocation(x, y);
                    }
                }
            }
        });
    }
    private final static int GUI_WIDTH = 2100;
    private final static int GUI_HEIGHT = 2400;
    private Game game;
    private int playerIndex;
    private CommandSender commandSender;
    private CardInfoFrame cardInfoFrame;
    private Bot bot;
    private CardChooserManager cardChooserManager = new CardChooserManager(){

        @Override
        public void setMessage(String text){
            message = text;
        }
    };
    private Graphics2D graphics;
    private final Font fontButton = new Font("Tahoma", Font.BOLD, 70);
    private final Font fontPlayerLifepoints = new Font("Tahoma", Font.BOLD, 70);
    private final Font fontDeckSize = new Font("Tahoma", Font.BOLD, 70);
    private static final Font fontTitle = new Font("Tahoma", Font.BOLD, 18);
    private static final Font fontDescription = new Font("Tahoma", Font.BOLD, 16);
    private static final Font fontStats = new Font("Tahoma", Font.BOLD, 35);
    private final Font fontMessage = new Font("Tahoma", Font.PLAIN, 90);
    private ConcurrentHashMap<Card, CardDisplay> cardDisplays = new ConcurrentHashMap<Card, CardDisplay>();
    private ArrayList<CardDisplay> sortedCardDisplays = new ArrayList<CardDisplay>();
    private CardDisplay hoveredCardDisplay;
    private CardDisplay draggedCardDisplay;
    private double offsetX;
    private double offsetY;
    private double scale;
    private Cursor cursorDefault = new Cursor(Cursor.DEFAULT_CURSOR);
    private Cursor cursorDrag = DragSource.DefaultMoveDrop;
    private Cursor cursorHand = new Cursor(Cursor.HAND_CURSOR);
    private int endTurnButtonX = 1800;
    private int endTurnButtonY = 1200;
    private boolean isEndTurnButtonHovered;
    private String message;
    private Comparator<CardDisplay> drawCardDisplayComparator = new Comparator<CardDisplay>(){

        @Override
        public int compare(CardDisplay cardDisplay1, CardDisplay cardDisplay2){
            return (cardDisplay1.getCard().getCardPosition().getIndex() - cardDisplay2.getCard().getCardPosition().getIndex());
        }
    };

    public void setGame(Game game){
        this.game = game;
        updateCardLocations();
    }

    public void setBot(Bot bot){
        this.bot = bot;
        bot.initialize(game, commandSender);
    }
    
    private boolean isInputAllowed(){
        return (game.isRunning() && (game.getCurrentPlayerIndex() == playerIndex));
    }
    
    private void updateHoverInformation(MouseEvent evt){
        int x = getGUI_X(evt.getX());
        int y = getGUI_Y(evt.getY());
        isEndTurnButtonHovered = ((x >= endTurnButtonX) && (y >= endTurnButtonY) && (x < endTurnButtonX + CardDisplay.WIDTH) && (y < endTurnButtonY + CardDisplay.HEIGHT));
        updateCursor();
    }
    
    private void updateCursor(){
        Cursor cursor = cursorDefault;
        if(draggedCardDisplay != null){
            cursor = cursorDrag;
        }
        else if(isEndTurnButtonHovered){
            cursor = cursorHand;
        }
        setCursor(cursor);
    }
    
    private boolean isDraggingActionAllowed(Card card){
        boolean isDraggingAllowed = false;
        if(card.getOwner().getIndex() == playerIndex){
            switch(card.getCardPosition().getZone()){
                case HAND:
                    if(card instanceof ManaProvider){
                        isDraggingAllowed = game.getPlayer(playerIndex).canAddMana();
                    }
                    if(card instanceof MonsterCard){
                        MonsterCard monsterCard = (MonsterCard) card;
                        isDraggingAllowed = game.getPlayer(playerIndex).canPay(monsterCard.getSummonCost(), monsterCard);
                    }
                    else if(card instanceof SpellCard){
                        SpellCard spellCard = (SpellCard) card;
                        isDraggingAllowed = game.getPlayer(playerIndex).canCast(spellCard.getSpell());
                    }
                    break;

                case MONSTER:
                    MonsterCard monsterCard = (MonsterCard) card;
                    isDraggingAllowed = monsterCard.canAttack();
                    break;
            }
        }
        return isDraggingAllowed;
    }
    
    private CardDisplay getHoveredCardDisplay(MouseEvent evt){
        int x = getGUI_X(evt.getX());
        int y = getGUI_Y(evt.getY());
        for(int i=(sortedCardDisplays.size()-1);i>=0;i--){
            CardDisplay cardDisplay = sortedCardDisplays.get(i);
            if((cardDisplay != draggedCardDisplay) && cardDisplay.intersects(x, y)){
                return cardDisplay;
            }
        }
        return null;
    }
    
    private int getGUI_X(int panelX){
        return (int) (((panelX - offsetX) / scale));
    }
    
    private int getGUI_Y(int panelY){
        return (int) (((panelY - offsetY) / scale));
    }
    
    public void update(float lastTimePerFrame){
        if(game != null){
            sortedCardDisplays = new ArrayList<CardDisplay>(cardDisplays.values());
            Collections.sort(sortedCardDisplays, drawCardDisplayComparator);
            updateCardLocations();
            for(CardDisplay cardDisplay : sortedCardDisplays){
                cardDisplay.update(lastTimePerFrame);
            }
        }
        updateUI();
    }
    
    private void updateCardLocations(){
        updateCardLocations(0);
        updateCardLocations(1);
    }
    
    private void updateCardLocations(int playerIndex){
        boolean isOwnPlayer = (playerIndex == this.playerIndex);
        Deck deck = game.getPlayer(playerIndex).getDeck();
        for(int i=0;i<deck.size();i++){
            Card card = deck.get(i);
            CardDisplay cardDisplay = getCardDisplay(card);
            cardDisplay.setIsFront(false);
            updateCardLocation(cardDisplay, 1800, (isOwnPlayer?2000:0));
        }
        Hand hand = game.getPlayer(playerIndex).getHand();
        int x = getCardsRow_Start(hand);
        for(int i=0;i<hand.size();i++){
            Card card = hand.get(i);
            CardDisplay cardDisplay = getCardDisplay(card);
            cardDisplay.setIsFront(isOwnPlayer);
            if(updateCardLocation(cardDisplay, x, (isOwnPlayer?2000:0))){
                x += getCardsRow_Interval(hand);
            }
        }
        ManaPool manaPool = game.getPlayer(playerIndex).getManaPool();
        x = getCardsRow_Start(manaPool);
        for(int i=0;i<manaPool.size();i++){
            Card card = manaPool.get(i);
            CardDisplay cardDisplay = getCardDisplay(card);
            cardDisplay.setIsFront(true);
            updateCardLocation(cardDisplay, x, (isOwnPlayer?1600:400));
            x += getCardsRow_Interval(manaPool);
        }
        Cards monsters = game.getPlayer(playerIndex).getMonsters();
        x = getCardsRow_Start(monsters);
        for(int i=0;i<monsters.size();i++){
            Card card = monsters.get(i);
            CardDisplay cardDisplay = getCardDisplay(card);
            cardDisplay.setIsFront(true);
            updateCardLocation(cardDisplay, x, (isOwnPlayer?1200:800));
            x += getCardsRow_Interval(monsters);
        }
        Graveyard graveyard = game.getPlayer(playerIndex).getGraveyard();
        for(int i=0;i<graveyard.size();i++){
            Card card = graveyard.get(i);
            CardDisplay cardDisplay = getCardDisplay(card);
            cardDisplay.setIsFront(true);
            updateCardLocation(cardDisplay, 1800, (isOwnPlayer?1600:400));
        }
    }
    
    private int getCardsRow_Start(Cards cards){
        int cardsSize = cards.size();
        if((cards instanceof Hand) && (draggedCardDisplay != null) && cards.contains(draggedCardDisplay.getCard())){
            cardsSize--;
        }
        return ((1800 - (Math.min(cardsSize, 6) * 300)) / 2);
    }
    
    private int getCardsRow_Interval(Cards cards){
        return (1800 / Math.max(6, cards.size()));
    }
    
    private CardDisplay getCardDisplay(Card card){
        CardDisplay cardDisplay = cardDisplays.get(card);
        if(cardDisplay == null){
            cardDisplay = new CardDisplay(card);
            cardDisplays.put(card, cardDisplay);
        }
        return cardDisplay;
    }
    
    private boolean updateCardLocation(CardDisplay cardDisplay, int targetX, int targetY){
        if(cardDisplay != draggedCardDisplay){
            cardDisplay.setTargetLocation(targetX, targetY);
            return true;
        }
        return false;
    }

    @Override
    protected void paintComponent(Graphics _graphics){
        super.paintComponent(_graphics);
        graphics = (Graphics2D) _graphics;
        graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/table.jpg"), 0, 0, getWidth(), getHeight(), this);
        graphics.translate(offsetX, offsetY);
        graphics.scale(scale, scale);
        graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/button" + (isEndTurnButtonHovered?"_hover":"") + ".png"), endTurnButtonX, endTurnButtonY, 300, 400, this);
        graphics.setFont(fontButton);
        graphics.setColor(Color.BLACK);
        if(game.getCurrentPlayerIndex() == playerIndex){
            if(cardChooserManager.getActiveCardChooser() != null){
                graphics.drawString("Cancel", endTurnButtonX + 33, endTurnButtonY + 225);
            }
            else{
                graphics.drawString("End", endTurnButtonX + 83, endTurnButtonY + 180);
                graphics.drawString("Turn", endTurnButtonX + 69, endTurnButtonY + 275);
            }
        }
        else{
            graphics.drawString("Enemy", endTurnButtonX + 30, endTurnButtonY + 180);
            graphics.drawString("Turn", endTurnButtonX + 69, endTurnButtonY + 275);
        }
        for(CardDisplay cardDisplay : sortedCardDisplays){
            if(cardDisplay != draggedCardDisplay){
                drawCardDisplay(cardDisplay);
            }
        }
        graphics.setFont(fontDeckSize);
        graphics.setColor(Color.WHITE);
        int deckSizePlayer = game.getPlayer(playerIndex).getDeck().size();
        int deckSizeEnemy = game.getPlayer(playerIndex).getEnemy().getDeck().size();
        graphics.drawString("" + deckSizePlayer, 1800 + ((deckSizePlayer < 10)?127:105), 2230);
        graphics.drawString("" + deckSizeEnemy, 1800 + ((deckSizeEnemy < 10)?127:105), 228);
        drawPlayerLifepoints();
        if(message != null){
            graphics.setFont(fontMessage);
            graphics.setColor(Color.WHITE);
            FontMetrics fontMetrics = graphics.getFontMetrics();
            Rectangle2D messageBounds = fontMetrics.getStringBounds(message, graphics);
            graphics.drawString(message, (int) ((2100 - messageBounds.getWidth()) / 2), 1028);
        }
        if(draggedCardDisplay != null){
            drawCardDisplay(draggedCardDisplay);
        }
    }
    
    private void drawPlayerLifepoints(){
        graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/player_lifepoints_player.png"), 50, 922, 100, 150, this);
        graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/player_lifepoints_enemy.png"), 1950, 922, 100, 150, this);
        graphics.setFont(fontPlayerLifepoints);
        graphics.setColor(Color.WHITE);
        int lifepointsPlayer = game.getPlayer(playerIndex).getLifepoints();
        int lifepointsEnemy = game.getPlayer(playerIndex).getEnemy().getLifepoints();
        graphics.drawString("" + lifepointsPlayer, 50 + ((lifepointsPlayer < 10)?26:6), 1035);
        graphics.drawString("" + lifepointsEnemy, 1950 + ((lifepointsEnemy < 10)?26:6), 1035);
    }
    
    private void drawCardDisplay(CardDisplay cardDisplay){
        graphics.translate(cardDisplay.getX(), cardDisplay.getY());
        AffineTransform tmpTransform = graphics.getTransform();
        if(cardDisplay.getCard().isTapped()){
            graphics.rotate(0.5, (CardDisplay.WIDTH / 2), (CardDisplay.HEIGHT / 2));
        }
        drawCardDisplay(graphics, this, cardDisplay);
        if(cardDisplay.isHighlighted()){
            graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/highlight_overlay.png"), 0, 0, 300, 400, this);
        }
        if(cardDisplay.getCard().isTapped()){
            graphics.setTransform(tmpTransform);
        }
        graphics.translate(-1 * cardDisplay.getX(), -1 * cardDisplay.getY());
    }
    
    public static void drawCardDisplay(Graphics2D graphics, ImageObserver imageObserver, CardDisplay cardDisplay){
        Card card = cardDisplay.getCard();
        if(cardDisplay.isFront()){
            if(card instanceof SimpleManaCard){
                SimpleManaCard simpleManaCard = (SimpleManaCard) card;
                graphics.setColor(ManaAmount.getColor(simpleManaCard.getMana()));
            }
            else{
                graphics.setColor(Color.WHITE);
            }
            graphics.fillRect(74, 43, 155, 155);
            String imageResourcePath = PanCardInfo.getCardImageResourcePath(card);
            graphics.drawImage(PanCard.getCachedImage(imageResourcePath), 74, 43, 155, 155, imageObserver);
            graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/templates/mana_" + card.getManaTypes()[0].ordinal() + ".png"), 0, 0, 300, 400, imageObserver);
            if(card instanceof MonsterCard){
                MonsterCard monsterCard = (MonsterCard) card;
                graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/templates/stats.png"), 0, 0, 300, 400, imageObserver);
                graphics.setFont(fontStats);
                graphics.setColor(Color.WHITE);
                graphics.drawString("" + monsterCard.getAttackDamage(), 38, 372);
                if(monsterCard.getCurrentLifepoints() < monsterCard.getMaximumLifepoints()){
                    graphics.setColor(Color.BLACK);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 - 1, 372 - 1);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 - 1, 372 + 1);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 - 1, 372 + 1);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 + 0, 372 - 1);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 + 0, 372 + 1);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 + 1, 372 - 1);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 + 1, 372 + 0);
                    graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242 + 1, 372 + 1);
                    graphics.setColor(Color.RED);
                }
                graphics.drawString("" + monsterCard.getCurrentLifepoints(), 242, 372);
            }
            graphics.setFont(fontTitle);
            graphics.setColor(Color.BLACK);
            FontMetrics fontMetrics = graphics.getFontMetrics();
            Rectangle2D titleBounds = fontMetrics.getStringBounds(card.getDescription().getTitle(), graphics);
            graphics.drawString(card.getDescription().getTitle(), 150 - (int) (titleBounds.getWidth() / 2),216);
            graphics.setFont(fontDescription);
            graphics.setColor(Color.BLACK);
            drawStringMultiLine(graphics, card.getDescription().getDescription(), 180, 64, 280);
        }
        else{
            graphics.drawImage(PanCard.getCachedImage("/cardgame/resources/images/back.png"), 0, 0, 300, 400, imageObserver);
        }
    }
    
    //http://stackoverflow.com/questions/4413132/problems-with-newline-in-graphics2d-drawstring
    public static void drawStringMultiLine(Graphics2D g, String text, int lineWidth, int x, int y) {
        FontMetrics m = g.getFontMetrics();
        if(m.stringWidth(text) < lineWidth) {
            g.drawString(text, x, y);
        } else {
            String[] words = text.split(" ");
            String currentLine = words[0];
            for(int i = 1; i < words.length; i++) {
                if(m.stringWidth(currentLine+words[i]) < lineWidth) {
                    currentLine += " "+words[i];
                } else {
                    g.drawString(currentLine, x, y);
                    y += m.getHeight();
                    currentLine = words[i];
                }
            }
            if(currentLine.trim().length() > 0) {
                g.drawString(currentLine, x, y);
            }
        }
    }

    public void setPlayerIndex(int playerIndex){
        this.playerIndex = playerIndex;
    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        addComponentListener(new java.awt.event.ComponentAdapter() {
            public void componentResized(java.awt.event.ComponentEvent evt) {
                formComponentResized(evt);
            }
        });

        javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
        this.setLayout(layout);
        layout.setHorizontalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 650, Short.MAX_VALUE)
        );
        layout.setVerticalGroup(
            layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGap(0, 450, Short.MAX_VALUE)
        );
    }// </editor-fold>//GEN-END:initComponents

    private void formComponentResized(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_formComponentResized
        scale = Math.min((((double) getWidth()) / GUI_WIDTH), (((double) getHeight()) / GUI_HEIGHT));
        offsetX = ((getWidth() - (scale * GUI_WIDTH)) / 2);
        offsetY = ((getHeight() - (scale * GUI_HEIGHT)) / 2);
    }//GEN-LAST:event_formComponentResized

    // Variables declaration - do not modify//GEN-BEGIN:variables
    // End of variables declaration//GEN-END:variables
}
